/**
 * Configuration Loader
 * 
 * Loads configuration from multiple sources with proper precedence
 */

import * as fs from 'fs/promises';
import * as path from 'path';
import { homedir } from 'os';
import { existsSync } from 'fs';
import { Logger } from '../utils/logger';
import { defaultConfig } from './defaults';
import { validateConfig } from './config-validator';
import { Config, ServerConfig } from '../types';

export class ConfigLoader {
  private logger = new Logger('ConfigLoader');
  private configPaths = [
    path.join(homedir(), '.mcp', 'gateway', 'config.json'),
    path.join(process.cwd(), 'config.json'),
    path.join(process.cwd(), '.mcp-gateway.json')
  ];

  async load(): Promise<Config> {
    // Start with defaults
    let config: Config = { ...defaultConfig };

    // Load from file
    const fileConfig = await this.loadFromFile();
    if (fileConfig) {
      config = this.mergeConfig(config, fileConfig);
    }

    // Override with environment variables
    config = this.loadFromEnv(config);

    // Validate final configuration
    const validation = validateConfig(config);
    if (!validation.valid) {
      throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
    }

    this.logger.info('Configuration loaded successfully');
    return config;
  }

  private async loadFromFile(): Promise<Partial<Config> | null> {
    for (const configPath of this.configPaths) {
      if (existsSync(configPath)) {
        try {
          const content = await fs.readFile(configPath, 'utf-8');
          const config = JSON.parse(content);
          this.logger.info(`Loaded configuration from ${configPath}`);
          return config;
        } catch (error) {
          this.logger.warn(`Failed to load config from ${configPath}:`, error);
        }
      }
    }
    return null;
  }

  private loadFromEnv(config: Config): Config {
    const envConfig = { ...config };

    // Gateway configuration
    if (process.env.MCP_GATEWAY_PORT) {
      envConfig.gateway.port = parseInt(process.env.MCP_GATEWAY_PORT, 10);
    }
    if (process.env.MCP_GATEWAY_HOST) {
      envConfig.gateway.host = process.env.MCP_GATEWAY_HOST;
    }

    // Discovery configuration
    if (process.env.MCP_AUTO_DISCOVER) {
      envConfig.discovery.enabled = process.env.MCP_AUTO_DISCOVER === 'true';
    }
    if (process.env.MCP_SERVERS_PATH) {
      envConfig.discovery.paths.push(process.env.MCP_SERVERS_PATH);
    }

    // Logging configuration
    if (process.env.MCP_LOG_LEVEL) {
      envConfig.logging.level = process.env.MCP_LOG_LEVEL as any;
    }
    if (process.env.MCP_LOG_FILE) {
      envConfig.logging.file = process.env.MCP_LOG_FILE;
    }

    return envConfig;
  }

  private mergeConfig(base: Config, override: Partial<Config>): Config {
    return {
      ...base,
      ...override,
      gateway: {
        ...base.gateway,
        ...(override.gateway || {})
      },
      discovery: {
        ...base.discovery,
        ...(override.discovery || {}),
        paths: [
          ...(base.discovery?.paths || []),
          ...(override.discovery?.paths || [])
        ]
      },
      servers: {
        ...base.servers,
        ...(override.servers || {})
      },
      logging: {
        ...base.logging,
        ...(override.logging || {})
      }
    };
  }

  async save(config: Config, filePath?: string): Promise<void> {
    const savePath = filePath || this.configPaths[0];
    const dir = path.dirname(savePath);

    // Ensure directory exists
    await fs.mkdir(dir, { recursive: true });

    // Save configuration
    await fs.writeFile(
      savePath,
      JSON.stringify(config, null, 2),
      'utf-8'
    );

    this.logger.info(`Configuration saved to ${savePath}`);
  }
}